<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="RevYn" id="RevYn"><span data-lake-id="uda0cdba8" id="uda0cdba8">典型回答</span></h1>
  <p data-lake-id="uf61dae5e" id="uf61dae5e"><br></p>
  <p data-lake-id="u03aeeee9" id="u03aeeee9"><span data-lake-id="ub28aa322" id="ub28aa322">所谓脑裂，就像他的名字一样，大脑裂开了，一般来说就是指一个分布式系统中有两个子集，然后每个子集都有一个自己的大脑(Leader/Master)。那么整个分布式系统中就会存在多个大脑了，而且每个自己都认为自己是正常的，从而导致数据不一致或重复写入等问题。</span></p>
  <p data-lake-id="uafce8033" id="uafce8033"><span data-lake-id="u5af779aa" id="u5af779aa">​</span><br></p>
  <p data-lake-id="u0239541c" id="u0239541c"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1692426824335-79c87ce7-0bf8-4141-986d-2c93e689d4ac.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_52%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <h3 data-lake-id="nusXl" id="nusXl"><span data-lake-id="u7bb57610" id="u7bb57610">脑裂的发生</span></h3>
  <p data-lake-id="ua77ab901" id="ua77ab901"><br></p>
  <p data-lake-id="u665ea37d" id="u665ea37d"><strong><span data-lake-id="u5fcf8c7e" id="u5fcf8c7e">Redis的脑裂问题可能发生在网络分区或者主节点出现问题的时候</span></strong><span data-lake-id="u508ef826" id="u508ef826">：</span></p>
  <p data-lake-id="u76cb2ec6" id="u76cb2ec6"><span data-lake-id="u0457037b" id="u0457037b">​</span><br></p>
  <ul list="u6f19e92f">
   <li fid="u9d9fc50e" data-lake-id="u22945f20" id="u22945f20"><strong><span data-lake-id="u4280bf30" id="u4280bf30">网络分区</span></strong><span data-lake-id="ue59e5c61" id="ue59e5c61">：网络故障或分区导致了不同子集之间的通信中断。</span></li>
  </ul>
  <ul list="u6f19e92f" data-lake-indent="1">
   <li fid="u9d9fc50e" data-lake-id="u8e524f83" id="u8e524f83"><span data-lake-id="ue986b621" id="ue986b621">Master节点，哨兵和Slave节点被分割为了两个网络，Master处在一个网络中，Slave库和哨兵在另外一个网络中，此时哨兵发现和Master连不上了，就会发起主从切换，选一个新的Master，这时候就会出现两个主节点的情况。</span></li>
  </ul>
  <ul list="u6f19e92f" start="2">
   <li fid="u9d9fc50e" data-lake-id="ub21f0827" id="ub21f0827"><strong><span data-lake-id="u57fa25df" id="u57fa25df">主节点问题</span></strong><span data-lake-id="u28118eb1" id="u28118eb1">：集群中的主节点之间出现问题，导致不同的子集认为它们是正常的主节点。</span></li>
  </ul>
  <ul list="u6f19e92f" data-lake-indent="1">
   <li fid="u9d9fc50e" data-lake-id="uca55c848" id="uca55c848"><span data-lake-id="u84c626dc" id="u84c626dc">Master节点有问题，哨兵就会开始选举新的主节点，但是在这个过程中，原来的那个Master节点又恢复了，这时候就可能会导致一部分Slave节点认为他是Master节点，而另一部分Slave新选出了一个Master</span></li>
  </ul>
  <p data-lake-id="uf5fc767d" id="uf5fc767d"><span data-lake-id="u7d4b549f" id="u7d4b549f">​</span><br></p>
  <h3 data-lake-id="nwTqc" id="nwTqc"><span data-lake-id="ua0ef0c34" id="ua0ef0c34">脑裂的危害</span></h3>
  <p data-lake-id="uc9f15101" id="uc9f15101"><span data-lake-id="u5b39d624" id="u5b39d624">脑裂问题可能导致以下</span><strong><u><span data-lake-id="u7dbdc484" id="u7dbdc484">问题</span></u></strong><span data-lake-id="u9f439d0a" id="u9f439d0a">：</span></p>
  <ul list="u490169ee">
   <li fid="u05140016" data-lake-id="u6c242c6c" id="u6c242c6c"><strong><span data-lake-id="u7d6efb10" id="u7d6efb10">数据不一致</span></strong><span data-lake-id="u601843a2" id="u601843a2">：不同子集之间可能对同一数据进行不同的写入，导致数据不一致。</span></li>
   <li fid="u05140016" data-lake-id="ub6d7d00e" id="ub6d7d00e"><strong><span data-lake-id="u45ce086c" id="u45ce086c">重复写入</span></strong><span data-lake-id="u7f2cbc2e" id="u7f2cbc2e">：在脑裂解决后，不同子集可能尝试将相同的写入操作应用到主节点上，导致数据重复。</span></li>
   <li fid="u05140016" data-lake-id="u5bd6f92f" id="u5bd6f92f"><strong><span data-lake-id="u78e77aae" id="u78e77aae">数据丢失</span></strong><span data-lake-id="u4ec25351" id="u4ec25351">：新选出来的Master会向所有的实例发送slave of命令，让所有实例重新进行全量同步，而全量同步首先就会将实例上的数据先清空，所以在主从同步期间在原来那个Master上执行的命令将会被清空。</span></li>
  </ul>
  <h3 data-lake-id="XQiDc" id="XQiDc"><br></h3>
  <h3 data-lake-id="kwGwS" id="kwGwS"><span data-lake-id="u6d2e09ed" id="u6d2e09ed">如何避免脑裂</span></h3>
  <p data-lake-id="u786abd26" id="u786abd26"><span data-lake-id="ub6610252" id="ub6610252">那么如何防止脑裂的发生呢？</span></p>
  <p data-lake-id="u715d4f3f" id="u715d4f3f"><span data-lake-id="u3cf4fbc4" id="u3cf4fbc4">​</span><br></p>
  <p data-lake-id="u0b9cf837" id="u0b9cf837"><span data-lake-id="u15a9fb92" id="u15a9fb92">Redis 已经提供了两个配置项可以帮我们做这个事儿，分别是 </span><code data-lake-id="u7e5bb502" id="u7e5bb502"><span data-lake-id="u57812591" id="u57812591">min-slaves-to-write</span></code><span data-lake-id="u2b09ac7f" id="u2b09ac7f"> 和 </span><code data-lake-id="uc5eec9c5" id="uc5eec9c5"><span data-lake-id="ucc6c3727" id="ucc6c3727">min-slaves-max-lag</span></code><span data-lake-id="u223862bd" id="u223862bd">。</span></p>
  <p data-lake-id="ua6d1d343" id="ua6d1d343"><span data-lake-id="u33312753" id="u33312753">​</span><br></p>
  <p data-lake-id="u029b0042" id="u029b0042"><code data-lake-id="u8d3dab8b" id="u8d3dab8b"><span data-lake-id="u35dee789" id="u35dee789">min-slaves-to-write</span></code><span data-lake-id="u2517027b" id="u2517027b">：主库能进行数据同步的最少从库数量；</span></p>
  <p data-lake-id="u8279c449" id="u8279c449"><code data-lake-id="u0ff53a5f" id="u0ff53a5f"><span data-lake-id="uf3832619" id="uf3832619">min-slaves-max-lag</span></code><span data-lake-id="u9a0c8cdc" id="u9a0c8cdc">：主从库间进行数据复制时，从库给主库发送 ACK 消息的最大延迟秒数。</span></p>
  <p data-lake-id="u56b9fb2a" id="u56b9fb2a"><span data-lake-id="uffaf7a33" id="uffaf7a33">​</span><br></p>
  <p data-lake-id="u7147939a" id="u7147939a"><span data-lake-id="u6a64f6bc" id="u6a64f6bc">这两个配置项必须同时满足，不然主节点拒绝写入。在期间满足min-slaves-to-write和min-slaves-max-lag的要求，那么主节点就会被禁止写入，脑裂造成的数据丢失情况自然也就解决了。</span></p>
  <p data-lake-id="u37c4b8f8" id="u37c4b8f8"><span data-lake-id="ue35540e0" id="ue35540e0">​</span><br></p>
  <p data-lake-id="u8494bed3" id="u8494bed3"><span data-lake-id="u02a344d4" id="u02a344d4">举个例子：</span></p>
  <p data-lake-id="u77633080" id="u77633080"><span data-lake-id="ubee46724" id="ubee46724">​</span><br></p>
  <p data-lake-id="u45f5876e" id="u45f5876e"><span data-lake-id="u4f5a47b7" id="u4f5a47b7">假设我们将 </span><code data-lake-id="u5eb1ff93" id="u5eb1ff93"><span data-lake-id="u55846edc" id="u55846edc">min-slaves-to-write</span></code><span data-lake-id="ucbf07af3" id="ucbf07af3"> 设置为 1，把</span><code data-lake-id="u6c454b46" id="u6c454b46"><span data-lake-id="ufe907c4b" id="ufe907c4b"> min-slaves-max-lag</span></code><span data-lake-id="u05490526" id="u05490526"> 设置为 10s。</span></p>
  <p data-lake-id="u7c7e1ab6" id="u7c7e1ab6"><span data-lake-id="u82c1527b" id="u82c1527b">​</span><br></p>
  <p data-lake-id="ube26eaa2" id="ube26eaa2"><span data-lake-id="uf9fc330c" id="uf9fc330c">如果Master节点因为某些原因挂了 12s，导致哨兵判断主库客观下线，开始进行主从切换。</span></p>
  <p data-lake-id="u75aab0aa" id="u75aab0aa"><span data-lake-id="u4c56eb8b" id="u4c56eb8b">​</span><br></p>
  <p data-lake-id="u73154f29" id="u73154f29"><span data-lake-id="u41cf7c3e" id="u41cf7c3e">同时，因为原Master宕机了 12s，没有一个（</span><code data-lake-id="u72fa139a" id="u72fa139a"><span data-lake-id="u16df2bfe" id="u16df2bfe">min-slaves-to-write</span></code><span data-lake-id="u812d678a" id="u812d678a">）从库能和原主库在 10s（ </span><code data-lake-id="uf9fcd0b3" id="uf9fcd0b3"><span data-lake-id="uc40c8484" id="uc40c8484">min-slaves-max-lag</span></code><span data-lake-id="ua2b1c7a5" id="ua2b1c7a5">） 内进行数据复制，这样一来，就因为不满足配置要求，原Master也就再也无法接收客户端请求了。</span></p>
  <p data-lake-id="uee05747a" id="uee05747a"><span data-lake-id="udee396c1" id="udee396c1">​</span><br></p>
  <p data-lake-id="u9d8f5e17" id="u9d8f5e17"><span data-lake-id="u00e3de26" id="u00e3de26">这样一来，主从切换完成后，也只有新主库能接收请求，这样就没有脑裂的发生了。</span></p>
  <p data-lake-id="u87fecba4" id="u87fecba4"><br></p>
  <h3 data-lake-id="aaY1J" id="aaY1J"><span data-lake-id="uc2cafeb5" id="uc2cafeb5">能彻底解决脑裂吗？</span></h3>
  <p data-lake-id="u42199e30" id="u42199e30"><br></p>
  <p data-lake-id="u4896246f" id="u4896246f"><span data-lake-id="ued70870e" id="ued70870e">还是刚刚那个场景，假设我们将 </span><code data-lake-id="u5628d6f5" id="u5628d6f5"><span data-lake-id="u04840d28" id="u04840d28">min-slaves-to-write</span></code><span data-lake-id="u14d9deef" id="u14d9deef"> 设置为 1，把</span><code data-lake-id="u81e04e91" id="u81e04e91"><span data-lake-id="u313aae19" id="u313aae19"> min-slaves-max-lag</span></code><span data-lake-id="ue5f1bd79" id="ue5f1bd79"> 设置为 10s，并且</span><code data-lake-id="u17ea93d7" id="u17ea93d7"><span data-lake-id="u0427ea08" id="u0427ea08">down-after-milliseconds</span></code><span data-lake-id="uc3f984e4" id="uc3f984e4">时间为8s，也就是说，如果8秒连不上主节点，哨兵就会进行主从切换。</span></p>
  <p data-lake-id="u7991c505" id="u7991c505"><span data-lake-id="u60a03e3b" id="u60a03e3b">​</span><br></p>
  <p data-lake-id="u37b75199" id="u37b75199"><span data-lake-id="ue9ee4874" id="ue9ee4874">但是，如果主从切换的过程需要5s时间的话，就会有问题。</span></p>
  <p data-lake-id="u3a18451a" id="u3a18451a"><span data-lake-id="uef322991" id="uef322991">​</span><br></p>
  <p data-lake-id="u5598071c" id="u5598071c"><span data-lake-id="uc90b6fe6" id="uc90b6fe6">Master节点宕机8s时，哨兵判断主节点客观下线，开始进行主从切换，但是这个过程一共需要5s。那如果主从切换过程中，主节点有恢复运行，即第9秒Master恢复了，而min-slaves-max-lag设置为10s那么主节点还是可写的。</span></p>
  <p data-lake-id="uf6976682" id="uf6976682"><span data-lake-id="u12ecf3cb" id="u12ecf3cb">​</span><br></p>
  <p data-lake-id="u7025b693" id="u7025b693"><span data-lake-id="u45eff42a" id="u45eff42a">那么就会导致9s~12s这期间如果有客户端写入原Master节点，那么这段时间的数据会等新的Master选出来之后，执行了slaveof之后导致丢失。</span></p>
  <p data-lake-id="uc41a2b08" id="uc41a2b08"><span data-lake-id="uf273ba20" id="uf273ba20">​</span><br></p>
  <p data-lake-id="ud594b10c" id="ud594b10c"><span data-lake-id="u40c4ffde" id="u40c4ffde">Redis脑裂可以采用min-slaves-to-write和min-slaves-max-lag合理配置尽量规避，但</span><strong><span data-lake-id="u2f1ae6d5" id="u2f1ae6d5">无法彻底解决</span></strong><span data-lake-id="u8fa2f1d0" id="u8fa2f1d0">，</span></p>
 </body>
</html>